﻿/////////////////////////////////////////////////////////////////
//
// Physics Helper XAML
//
// by Andy Beaulieu - http://www.andybeaulieu.com
//
// LICENSE: This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ANY 
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS
// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
/////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts;
using FarseerPhysics.Dynamics.Joints;
using FarseerPhysics.Factories;
using Microsoft.Xna.Framework;
using System.Threading.Tasks;
using Windows.Foundation;
using sw = Windows.Foundation;
using WinUI = Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using SysWinMedia = Windows.UI.Xaml.Media;
using UIShapes = Windows.UI.Xaml.Shapes;
using Windows.UI.Xaml.Media;
using xna = Microsoft.Xna.Framework;
using FarseerPhysics.Collision.Shapes;



namespace Spritehand.FarseerHelper
{
    /// <summary>
    /// A Sprite in the physics simulation, consisting of a Body and Geometry (boundary)
    /// </summary>
    public class PhysicsSprite : Canvas
    {
        private Body _bodyObject;
        private SysWinMedia.CompositeTransform Transform { get; set; }
        private int _X;
        private int _Y;
        private float _rotation;
        private UIShapes.Shape _pathDebug { get; set; }
        private UIShapes.Shape _pathDebugResting { get; set; }

        private UIShapes.Ellipse _ellipseFocus { get; set; }
        public bool _focusMode = false;

        private Vector2 _posMouseMoveStart;
        private sw.Point _ptMouseDown, _ptMouseDownSprite;
        private float _originalAngularDamping;
        private bool _bodyWasStatic = false;

        public delegate void CollisionHandler(PhysicsSprite source, string collidedWith, Contact contact);
        /// <summary>
        /// event for Collision between geometries
        /// </summary>
        public event CollisionHandler Collision;

        public delegate void MousePickStartedHandler(PhysicsSprite source, Vector2 ptMoveStart);
        /// <summary>
        /// event for Drag/Drop begin
        /// </summary>
        public event MousePickStartedHandler MousePickStarted;


        public delegate void MousePickCompletedHandler(PhysicsSprite source, Vector2 ptMoveStart);
        /// <summary>
        /// event for Drag/Drop complete
        /// </summary>
        public event MousePickCompletedHandler MousePickCompleted;

        public delegate void DisabledHandler(PhysicsSprite source);
        /// <summary>
        /// event fired when sprite becomes disabled
        /// </summary>
        public event DisabledHandler Disabled;


        // mouse pick
        public FixedMouseJoint _mousePickSpring;
        UIShapes.Line _lineShowSpring;
        bool _draggingNonPhysicsSprite = false;

        PhysicsCanvas _physicsCanvas;
        public PhysicsCanvas GameCanvas { get { return _physicsCanvas; } }

        // if this sprite is an Image type, then store the Pixel Data, height, width
        public byte[] _imgPixelData;
        public Size _imgSize;


        private float _frictionCoefficient = 0.4f;
        /// <summary>
        /// The amount of friction for this sprite
        /// </summary>
        /// 
        public double FrictionCoefficient
        {
            get
            {
                return _frictionCoefficient;
            }
            set
            {
                _frictionCoefficient = (float)value;

                if (BodyObject != null)
                {
                    foreach (Fixture f in BodyObject.FixtureList)
                    {
                        f.Friction = _frictionCoefficient;
                    }
                }
            }
        }

        private bool _collisionEventDisable = false;
        /// <summary>
        /// If true then collision events will not be fired for this sprite
        /// </summary>
        public bool CollisionEventDisable
        {
            get { return _collisionEventDisable; }
            set
            {
                _collisionEventDisable = value;

                if (BodyObject != null)
                {
                    foreach (Fixture f in BodyObject.FixtureList)
                    {
                        f.OnCollision -= HandleOnCollision;
                        if (_collisionEventDisable == false)
                        {
                            f.OnCollision += new OnCollisionEventHandler(HandleOnCollision);
                        }
                    }
                }
            }
        }

        private bool _isLoaded;
        /// <summary>
        /// If true then this sprite has been loaded into the visual Game Canvas
        /// </summary>
        public bool IsLoaded
        {
            get
            {
                return _isLoaded;
            }
            set
            {
                _isLoaded = value;

            }
        }

        private bool _isStatic;
        /// <summary>
        /// If true then this sprite stays in place
        /// </summary>
        public bool IsStatic
        {
            get
            {
                return _isStatic;
            }
            set
            {
                _isStatic = value;

                if (BodyObject != null)
                {
                    BodyObject.IsStatic = value;
                }
            }
        }

        private bool _allowStaticObjectManipulation = false;
        /// <summary>
        /// If true then collision events will not be fired for this sprite
        /// </summary>
        public bool AllowStaticObjectManipulation
        {
            get { return _allowStaticObjectManipulation; }
            set
            {
                _allowStaticObjectManipulation = value;
            }
        }

        private bool _isSensor;
        /// <summary>
        /// If true then this sprite stays in place
        /// </summary>
        public bool IsSensor
        {
            get
            {
                return _isSensor;
            }
            set
            {
                _isSensor = value;

                if (BodyObject != null)
                {
                    BodyObject.IsSensor = value;
                }
            }
        }

        private double _effectiveDamage;
        /// <summary>
        /// Stores the effective damage from any Collisions
        /// </summary>
        public double EffectiveDamage
        {
            get
            {
                return _effectiveDamage;
            }
            set
            {
                _effectiveDamage = value;
            }
        }

        private object _userData;
        /// <summary>
        /// Optional user-specific data for the sprite
        /// </summary>
        public Object UserData
        {
            get
            {
                return _userData;
            }
            set
            {
                _userData = value;
            }
        }

        private string _originalName;
        /// <summary>
        /// Saves the original name (before forcing uniqueness)
        /// </summary>
        public string OriginalName
        {
            get
            {
                return _originalName;
            }
            set
            {
                _originalName = value;
            }
        }

        private bool _isEnabled = true;
        /// <summary>
        /// If true then this sprite takes place in the simulation.
        /// </summary>
        public bool IsEnabled
        {
            get
            {
                return _isEnabled;
            }
            set
            {
                _isEnabled = value;

                if (BodyObject != null)
                {
                    if (_isEnabled)
                    {
                        _bodyObject.Enabled = true;
                        this.Visibility = Visibility.Visible;
                    }
                    else
                    {
                        _bodyObject.Enabled = false;
                        this.Visibility = Visibility.Collapsed;
                        if (Disabled != null)
                            Disabled(this);
                    }
                }

            }
        }

        private bool _isBodyEnabled = true;
        /// <summary>
        /// If true then this sprite is visible but the physics is disabled.
        /// </summary>
        public bool IsBodyEnabled
        {
            get
            {
                return _isBodyEnabled;
            }
            set
            {
                _isBodyEnabled = value;

                if (BodyObject != null)
                {
                    if (_isBodyEnabled)
                    {
                        _bodyObject.Enabled = true;
                    }
                    else
                    {
                        _bodyObject.Enabled = false;
                    }
                }

            }
        }


        /// <summary>
        /// Use Rectangle or Ellipse for best performance. Use Polygon when you need precise outline shapes.
        /// </summary>
        public enum ShapeTypes
        {
            Rectangle,
            Ellipse,
            Polygon,
            Image
        }


        // HACK: Beta bits do NOT allow Enums in XAML, causes code generation error
        // so we will use String and convert to enum
        private ShapeTypes _shapeType;
        public string ShapeType
        {
            get
            {
                return _shapeType.ToString();
            }
            set
            {
                _shapeType = (ShapeTypes)Enum.Parse(typeof(ShapeTypes), value, true);
            }
        }

        private DateTime _dtCtor;

        public PhysicsSprite()
        {
            if (Utilities.IsDesignMode)
                return;

            this.CacheMode = new BitmapCache();

            _dtCtor = DateTime.Now;
            this.Loaded += new RoutedEventHandler(PhysicsSprite_Loaded);
        }

        async void PhysicsSprite_Loaded(object sender, RoutedEventArgs e)
        {
            // set up the transform for position and rotation
            if (this.RenderTransform != null && this.RenderTransform is SysWinMedia.TransformGroup)
            {
                foreach (SysWinMedia.Transform trn in (this.RenderTransform as SysWinMedia.TransformGroup).Children)
                {
                    if (trn is SysWinMedia.CompositeTransform)
                        Transform = trn as SysWinMedia.CompositeTransform;
                }
            }
            else
                if (this.RenderTransform is SysWinMedia.CompositeTransform)
            {
                Transform = this.RenderTransform as SysWinMedia.CompositeTransform;
            }
            else
            {
                this.RenderTransform = new SysWinMedia.CompositeTransform();
                Transform = this.RenderTransform as SysWinMedia.CompositeTransform;
            }

            if (_physicsCanvas == null)
            {
                _physicsCanvas = PhysicsUtilities.FindPhysicsCanvas(this);

                if (_physicsCanvas == null)
                {
                    System.Diagnostics.Debug.WriteLine("PhysicsCanvas was not found - sprite will not be loaded");
                    return;
                }
            }

            if (InitializePhysics)
            {
                List<FarseerPhysics.Collision.Shapes.Shape> shapes;

                try
                {
                    shapes = await PhysicsCanvas.BoundaryHelper.CreateShapesFromElement(_physicsCanvas.Simulator, this, _shapeType);
                }
                catch (Exception ex)
                {
                    shapes = await PhysicsCanvas.BoundaryHelper.CreateShapesFromElement(_physicsCanvas.Simulator, this, ShapeTypes.Rectangle);
                }


                // DEMO: (3) Create the Body based on shape (outline)
                BodyObject = BodyFactory.CreateBody(_physicsCanvas.Simulator);
                BodyObject.BodyType = BodyType.Dynamic;

                foreach (FarseerPhysics.Collision.Shapes.Shape shape in shapes)
                {
                    BodyObject.CreateFixture(shape);
                }

                foreach (Fixture f in BodyObject.FixtureList)
                {
                    if (_collisionEventDisable)
                    {
                        System.Diagnostics.Debug.WriteLine(this.GetValue(Canvas.NameProperty) + " WILL NOT fire collision events.");
                        CollisionEventDisable = _collisionEventDisable;
                    }
                }


                BodyObject.Awake = true;

                // did this item have a rotation if so then we need to add it back into new sprite?
                double angle = 0;

                if (this.RenderTransform is SysWinMedia.CompositeTransform)
                {
                    angle = (this.RenderTransform as SysWinMedia.CompositeTransform).Rotation;
                    (this.RenderTransform as SysWinMedia.CompositeTransform).Rotation = 0;
                    this.RenderTransformOrigin = new sw.Point(0, 0);
                }

                if (angle != 0)
                {
                    float radAngle = (float)(Math.PI * angle / 180.0);
                    BodyObject.Rotation = radAngle;
                    _rotation = radAngle;
                }

                // refresh static now that body is created
                IsStatic = _isStatic;
                FrictionCoefficient = _frictionCoefficient;
                CollisionGroup = _collisionGroup;
                CausesCollisions = _causesCollisions;
                DebugMode = _debugMode;
                RestitutionCoefficient = _restitutionCoefficient;
                CollisionEventDisable = _collisionEventDisable;
                InternalName = this.Name;
                DebugMode = _physicsCanvas.DebugMode;
                IsBullet = _isBullet;
                IsEnabled = _isEnabled;
                IsBodyEnabled = _isBodyEnabled;
                Rotation = PhysicsUtilities.RadiansToDecimalDegrees(_rotation);
                AngularDamping = _angularDamping;
                LinearDamping = _linearDamping;

                if (_mass == -1)
                    _mass = BodyObject.Mass;
                else
                    Mass = _mass;


                IsSensor = _isSensor;

                // get the overall dimensions
                double width, height;
                width = this.ActualWidth;
                height = this.ActualHeight;


                // adjust the offset position in the world
                xna.Vector2 offsetPosition = PhysicsUtilities.GetOffsetPositionInScreen(this);

                // adjust position to take into consideration origin at middle 
                offsetPosition.X = (offsetPosition.X + ((float)getWidth / 2));
                offsetPosition.Y = (offsetPosition.Y + ((float)getHeight / 2));

                if (_position != Vector2.Zero)
                    Position = _position;
                else
                    BodyObject.Position = PhysicsCanvas.BoundaryHelper.ScreenToWorld(offsetPosition);

                Update();




            }

            // add this sprite into the simulation
            if (_physicsCanvas != null)
            {
                _physicsCanvas.AddPhysicsSprite(this, InitializePhysics);
            }

            this.Loaded -= PhysicsSprite_Loaded;

            IsLoaded = true;

            //System.Diagnostics.Debug.WriteLine("added sprite {0} in {1} ms. with mass={2}", this.Name, (DateTime.Now - _dtCtor).TotalMilliseconds, _mass);
        }

        public void SetPhysicsCanvas(PhysicsCanvas cnvGame)
        {
            _physicsCanvas = cnvGame;
        }

        private bool HandleOnCollision(Fixture fixtureA, Fixture fixtureB, Contact contact)
        {
            if (Collision != null)
            {
                if (fixtureA.Body == this.BodyObject)
                {
                    // add in damage
                    EffectiveDamage += fixtureA.Body.GetLinearVelocityFromLocalPoint(contact.Manifold.LocalPoint).Length()
                        + fixtureB.Body.GetLinearVelocityFromLocalPoint(contact.Manifold.LocalPoint).Length();
                    Collision(this, Convert.ToString(fixtureB.UserData), contact);
                }
            }

            return true;

        }

        private bool _mousePickEnabled;
        /// <summary>
        /// If true then the mouse (or gestures) can be used to manipulate the sprite
        /// </summary>
        public bool MousePickEnabled
        {
            get
            {
                return _mousePickEnabled;
            }
            set
            {
                if (_mousePickEnabled != value)
                {
                    _mousePickEnabled = value;

                    if (_mousePickEnabled)
                    {
                        this.ManipulationMode = ManipulationModes.TranslateX | ManipulationModes.TranslateY | ManipulationModes.Rotate;
                        this.ManipulationStarted += new ManipulationStartedEventHandler(sprite_ManipulationStarted);
                        this.ManipulationDelta += new ManipulationDeltaEventHandler(sprite_ManipulationDelta);
                        this.ManipulationCompleted += new ManipulationCompletedEventHandler(sprite_ManipulationCompleted);

                        // add a pick spring if selected by user
                        _lineShowSpring = new UIShapes.Line();
                        _lineShowSpring.Stroke = new SysWinMedia.SolidColorBrush(WinUI.Colors.Black);
                        _lineShowSpring.Visibility = Visibility.Collapsed;
                        _physicsCanvas.Children.Add(_lineShowSpring);
                    }
                    else
                    {
                        this.ManipulationMode = ManipulationModes.System;
                        this.ManipulationStarted -= (sprite_ManipulationStarted);
                        this.ManipulationDelta -= (sprite_ManipulationDelta);
                        this.ManipulationCompleted -= (sprite_ManipulationCompleted);
                        if (_lineShowSpring != null)
                        {
                            _physicsCanvas.Children.Remove(_lineShowSpring);
                            _lineShowSpring = null;
                        }
                    }
                }
            }
        }



        void sprite_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)
        {
            _ptMouseDownSprite = e.Position;
            HandleMouseDown(TransformToCanvas(sender as UIElement, e.Position));
        }

        void sprite_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
        {


            HandleMouseMove(TransformToCanvas(sender as UIElement, e.Position), e.Delta.Rotation);

        }

        void sprite_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
        {
            HandleMouseUp(TransformToCanvas(sender as UIElement, e.Position));
        }

        Point TransformToCanvas(UIElement elem, Point pt)
        {
            GeneralTransform gt = elem.TransformToVisual(_physicsCanvas);
            return gt.TransformPoint(pt);
        }

        public Point TransformFromCanvas(UIElement elem, Point pt)
        {
            GeneralTransform gt = _physicsCanvas.TransformToVisual(elem);
            return gt.TransformPoint(pt);
        }

        public void HandleMouseDown(sw.Point pt)
        {
            DestroyPickSpring();

            _posMouseMoveStart = this.Position;
            _ptMouseDown = TransformFromCanvas(this, pt);

            if (MousePickEnabled && BodyObject == null)
            {
                // "scenery" type element
                _draggingNonPhysicsSprite = true;
                if (MousePickStarted != null)
                    MousePickStarted(this, this.Position);

                return;
            }

            if (MousePickEnabled && BodyObject.IsStatic && AllowStaticObjectManipulation && BodyObject.Enabled)
            {
                // save the static attributes
                _originalAngularDamping = BodyObject.AngularDamping;
                BodyObject.AngularDamping = float.MaxValue;
                _bodyWasStatic = true;
                BodyObject.IsStatic = false;
            }

            if (MousePickEnabled && BodyObject.IsStatic == false && BodyObject.Enabled)
            {
                CreatePickSpring(pt);

                if (MousePickStarted != null)
                    MousePickStarted(this, this.Position);

            }

        }

        public void HandleMouseMove(sw.Point pt, double rotation)
        {
            if (_draggingNonPhysicsSprite)
            {
                Canvas.SetLeft(this, pt.X - _ptMouseDownSprite.X);
                Canvas.SetTop(this, pt.Y - _ptMouseDownSprite.Y);
            }
            else
                if (_mousePickSpring != null)
            {
                //pt = new Point(pt.X / _physicsCanvas.TransformRoot.ScaleX, pt.Y / _physicsCanvas.TransformRoot.ScaleY);

                xna.Vector2 pointScreen = new xna.Vector2((float)(pt.X), (float)(pt.Y));

                xna.Vector2 point = PhysicsCanvas.BoundaryHelper.ScreenToWorld(pointScreen);
                _mousePickSpring.WorldAnchorB = point;

                if (rotation != 0)
                {
                    _mousePickSpring.BodyA.Rotation += (float)(rotation);
                    //System.Diagnostics.Debug.WriteLine("rotated " + rotation + " deg.");
                }

                _lineShowSpring.X2 = pointScreen.X;
                _lineShowSpring.Y2 = pointScreen.Y;
                _lineShowSpring.Visibility = Visibility.Visible;
            }
        }

        public void HandleMouseUp(sw.Point pt)
        {
            if (_draggingNonPhysicsSprite)
            {
                // "scenery" type element
                _draggingNonPhysicsSprite = false;
                if (MousePickCompleted != null)
                    MousePickCompleted(this, _posMouseMoveStart);

                return;
            }

            DestroyPickSpring();

        }

        public void DestroyPickSpring()
        {
            if (_mousePickSpring != null)
            {
                _mousePickSpring.Enabled = false;
                _physicsCanvas.Simulator.RemoveJoint(_mousePickSpring);
                _mousePickSpring = null;

                if (_lineShowSpring != null)
                {
                    _lineShowSpring.Visibility = Visibility.Collapsed;
                }


                if (AllowStaticObjectManipulation && _bodyWasStatic)
                {
                    // save the static attributes
                    BodyObject.AngularDamping = _originalAngularDamping;
                    BodyObject.IsStatic = true;
                    _bodyWasStatic = false;
                }

                if (MousePickCompleted != null)
                    MousePickCompleted(this, _posMouseMoveStart);
            }
        }

        public void CreatePickSpring(sw.Point ptMouse)
        {
            xna.Vector2 pointScreen = new xna.Vector2((float)(ptMouse.X), (float)(ptMouse.Y));

            xna.Vector2 point = PhysicsCanvas.BoundaryHelper.ScreenToWorld(pointScreen);
            _mousePickSpring = new FixedMouseJoint(this.BodyObject, point);
            _mousePickSpring.MaxForce = 1000.0f * this.BodyObject.Mass;
            _mousePickSpring.Enabled = true;

            _physicsCanvas.Simulator.AddJoint(_mousePickSpring);
            this.BodyObject.Awake = true;




            xna.Vector2 pointBody = PhysicsCanvas.BoundaryHelper.WorldToScreen(this.BodyObject.Position);
            _lineShowSpring.X1 = pointBody.X;
            _lineShowSpring.Y1 = pointBody.Y;
            _lineShowSpring.X2 = pointScreen.X;
            _lineShowSpring.Y2 = pointScreen.Y;
            _lineShowSpring.Visibility = Visibility.Visible;
        }

        public void UpdatePickSpring()
        {
            // show the pick spring
            if (_lineShowSpring != null && _lineShowSpring.Visibility != Visibility.Collapsed)
            {
                xna.Vector2 pt = PhysicsCanvas.BoundaryHelper.WorldToScreen(_mousePickSpring.WorldAnchorA);
                _lineShowSpring.X1 = pt.X;
                _lineShowSpring.Y1 = pt.Y;
            }
        }

        private string _internalName;
        /// <summary>
        /// the Name of the sprite inside UserData - this is used later for collisions
        /// and determining which sprite was hit by fixture
        /// </summary>
        public string InternalName
        {
            get
            {
                return _internalName;
            }
            set
            {
                _internalName = value;
                if (BodyObject != null)
                {
                    foreach (Fixture f in BodyObject.FixtureList)
                    {
                        f.UserData = _internalName;
                    }
                    this.BodyObject.UserData = _internalName;
                }
            }
        }

        /// <summary>
        /// The width, in pixels, of the sprite
        /// </summary>
        public virtual double getWidth
        {
            get { return this.Width; }
            set { this.Width = value; }
        }

        /// <summary>
        /// The height, in pixels, of the sprite
        /// </summary>
        public virtual double getHeight
        {
            get { return this.Height; }
            set { this.Height = value; }
        }

        private float _mass = -1;
        /// <summary>
        /// Mass of the sprite object
        /// </summary>
        public double Mass
        {
            get { return Convert.ToDouble(_mass); }
            set
            {
                _mass = (float)value;
                if (BodyObject != null)
                {
                    BodyObject.Mass = _mass;
                }
            }
        }

        private xna.Vector2 _position = Vector2.Zero;
        public xna.Vector2 Position
        {
            get
            {
                if (BodyObject != null)
                {
                    return PhysicsCanvas.BoundaryHelper.WorldToScreen(BodyObject.Position);
                }
                else
                {
                    float left = (float)(Canvas.GetLeft(this) + (this.Width / 2d));
                    float top = (float)(Canvas.GetTop(this) + (this.Height / 2d));

                    return new Vector2(left, top);
                }
            }
            set
            {
                _position = value;

                Vector2 pos = PhysicsCanvas.BoundaryHelper.ScreenToWorld(value);

                if (BodyObject != null)
                {
                    BodyObject.Position = pos;
                    BodyObject.AngularVelocity = 0f;
                    BodyObject.LinearVelocity = Vector2.Zero;
                }
                else
                {
                    double left = (_position.X - (this.Width / 2d));
                    double top = (_position.Y - (this.Height / 2d));

                    Canvas.SetLeft(this, left);
                    Canvas.SetTop(this, top);
                }

            }
        }

        public double Rotation
        {
            get
            {
                if (BodyObject != null)
                    return PhysicsUtilities.RadiansToDecimalDegrees(_rotation);
                else
                    if (Transform != null)
                    return Transform.Rotation;
                else
                    return PhysicsUtilities.RadiansToDecimalDegrees(_rotation);
            }
            set
            {
                _rotation = (float)PhysicsUtilities.DecimalDegreesToRadians(value);

                if (BodyObject != null)
                {
                    BodyObject.Rotation = _rotation;
                    Transform.Rotation = (_rotation * 360) / (2 * Math.PI);
                }
                else
                {
                    if (Transform != null)
                        Transform.Rotation = value;
                }
            }
        }

        private bool _isBullet = false;
        /// <summary>
        /// If true the sprite is treated as a bullet in continuous collision detection
        /// </summary>
        public bool IsBullet
        {
            get { return _isBullet; }
            set
            {
                _isBullet = value;
                if (BodyObject != null)
                {
                    BodyObject.IsBullet = _isBullet;
                }
            }
        }

        private bool _initializePhysics = true;
        /// <summary>
        /// If true the sprite is initialized on Load.
        /// </summary>
        public bool InitializePhysics
        {
            get { return _initializePhysics; }
            set
            {
                _initializePhysics = value;
            }
        }

        /// <summary>
        /// The Body element for Physics simulations
        /// </summary>
        public Body BodyObject
        {
            get
            {
                return _bodyObject;
            }
            set
            {
                _bodyObject = value;

                SetTransformCenter();
            }
        }

        public void SetTransformCenter()
        {
            // set up our translation transform - this is needed because Farseer uses a centered origin instead of top-left coordinates
            Transform.TranslateX = -getWidth / 2;
            Transform.TranslateY = -getHeight / 2;

            // set up our rotation transform - this is so Farseer can rotate the object as necessary
            Transform.CenterX = getWidth / 2;
            Transform.CenterY = getHeight / 2;
        }

        private short _collisionGroup = 0;
        /// <summary>
        /// Sprites in the same CollisionGroup DO NOT collide.
        /// </summary>
        public int CollisionGroup
        {
            get { return _collisionGroup; }
            set
            {
                _collisionGroup = Convert.ToInt16(value);

                if (_collisionGroup > 0 && _bodyObject != null)
                {

                    foreach (Fixture f in BodyObject.FixtureList)
                    {
                        f.CollisionGroup = _collisionGroup;
                    }
                }

            }
        }

        private bool _causesCollisions = true;
        /// <summary>
        /// Enables/disables collisions while retainining physics properties
        /// </summary>
        public bool CausesCollisions
        {
            get { return _causesCollisions; }
            set
            {
                _causesCollisions = value;

                if (_bodyObject != null)
                {

                    foreach (Fixture f in (_bodyObject.FixtureList))
                    {
                        if (_causesCollisions)
                            f.CollidesWith = Category.All;
                        else
                            f.CollidesWith = Category.None;

                    }
                }

            }
        }

        private bool _debugMode;
        /// <summary>
        /// If true then an outline of the sprites boundary shape is shown on screen
        /// </summary>
        public bool DebugMode
        {
            get
            {
                return _debugMode;
            }
            set
            {
                _debugMode = value;


                if (_debugMode)
                {
                    _pathDebug = PhysicsUtilities.CreatePathFromVertices(BodyObject, this, new WinUI.Color() { A = 192, R = 92, G = 192, B = 92 });
                    //_pathDebug.Opacity = 0.7;
                    this.Children.Add(_pathDebug);

                    _pathDebugResting = PhysicsUtilities.CreatePathFromVertices(BodyObject, this, new WinUI.Color() { A = 192, R = 192, G = 92, B = 92 });
                    //_pathDebugResting.Opacity = 0.7;
                    this.Children.Add(_pathDebugResting);
                }
                else
                {
                    if (_pathDebug != null)
                    {
                        this.Children.Remove(_pathDebug);
                        _pathDebug = null;
                    }
                    if (_pathDebugResting != null)
                    {
                        this.Children.Remove(_pathDebugResting);
                        _pathDebugResting = null;
                    }
                }


            }
        }

        private double _restitutionCoefficient;
        /// <summary>
        /// The amount of "bounce" (larger number == more bounce)
        /// </summary>
        public double RestitutionCoefficient
        {
            get { return _restitutionCoefficient; }
            set
            {
                _restitutionCoefficient = value;

                if (BodyObject != null)
                {
                    foreach (Fixture f in BodyObject.FixtureList)
                    {
                        f.Restitution = (float)_restitutionCoefficient;
                    }

                }
            }
        }

        private double _angularDamping;
        /// <summary>
        /// Increase this to make the sprite harder to rotate due to forces applied.
        /// </summary>
        public double AngularDamping
        {
            get { return _angularDamping; }
            set
            {
                _angularDamping = value;

                if (BodyObject != null)
                {
                    BodyObject.AngularDamping = (float)_angularDamping;

                }
            }
        }

        private double _linearDamping;
        /// <summary>
        /// Increase this to make the sprite harder to move due to forces applied.
        /// </summary>
        public double LinearDamping
        {
            get { return _linearDamping; }
            set
            {
                _linearDamping = value;

                if (BodyObject != null)
                {
                    BodyObject.LinearDamping = (float)_linearDamping;

                }
            }
        }


        private UserControl _userControlParent;
        public UserControl UserControlParent
        {
            get
            {
                return _userControlParent;
            }
            set
            {
                _userControlParent = value;
            }
        }

        /// <summary>
        /// This method must be called to keep the sprite position and rotation in sync
        /// with the Farseer Physics object body.
        /// </summary>
        public virtual void Update()
        {
            if (_focusMode)
            {
                if (_ellipseFocus.Width > this.Width || _ellipseFocus.Height > this.Height)
                {
                    _ellipseFocus.Width = 0;
                    _ellipseFocus.Height = 0;
                }

                _ellipseFocus.Height += 2;
                _ellipseFocus.Width += 2;
                Canvas.SetLeft(_ellipseFocus, this.Width / 2d - _ellipseFocus.Width / 2d);
                Canvas.SetTop(_ellipseFocus, this.Height / 2d - _ellipseFocus.Height / 2d);

            }

            if (float.IsNaN(BodyObject.Position.X) || float.IsNaN(BodyObject.Position.Y))
                return;

            xna.Vector2 pos = PhysicsCanvas.BoundaryHelper.WorldToScreen(BodyObject.Position);

            int newX = Convert.ToInt32(pos.X);
            if (newX != _X)
            {
                _X = newX;
                Canvas.SetLeft(this, Convert.ToDouble(_X));
            }

            int newY = Convert.ToInt32(pos.Y);
            if (newY != _Y)
            {
                _Y = newY;
                Canvas.SetTop(this, Convert.ToDouble(_Y));
            }

            if (Math.Abs(_rotation - BodyObject.Rotation) > 0.017f)
            {
                _rotation = BodyObject.Rotation;
                Transform.Rotation = (_rotation * 360) / (2 * Math.PI);
            }

            if (_debugMode)
            {
                if (BodyObject.Awake)
                {
                    _pathDebug.Opacity = 1;
                    _pathDebugResting.Opacity = 0;
                }
                else
                {
                    _pathDebug.Opacity = 0;
                    _pathDebugResting.Opacity = 1;
                }
            }

            //System.Diagnostics.Debug.WriteLine(this.Name + ": X=" + _X.ToString() + " Y=" + _Y.ToString() + " Rotation=" + RotationTransform.Angle);
        }

        /// <summary>
        /// Called when a sprite is deleted to clean up resources
        /// </summary>
        public void Cleanup()
        {
            this.ManipulationStarted -= (sprite_ManipulationStarted);
            this.ManipulationDelta -= (sprite_ManipulationDelta);
            this.ManipulationCompleted -= (sprite_ManipulationCompleted);

            if (_lineShowSpring != null)
            {
                _physicsCanvas.Children.Remove(_lineShowSpring);
                _lineShowSpring = null;
            }

            CleanupCollisionHandler();
        }

        public void CleanupCollisionHandler()
        {
            foreach (Fixture f in BodyObject.FixtureList)
            {
                f.OnCollision -= HandleOnCollision;
            }
        }

        public void ShowFocusAnimation(bool show)
        {
            if (show)
            {
                _focusMode = true;
                _ellipseFocus = new UIShapes.Ellipse();
                _ellipseFocus.Stroke = new SolidColorBrush(WinUI.Colors.Yellow);
                _ellipseFocus.StrokeThickness = 1;
                CompositeTransform transform = new CompositeTransform();
                transform.CenterX = this.Width / 2d;
                transform.CenterY = this.Height / 2d;
                _ellipseFocus.Width = 10;
                _ellipseFocus.Height = 10;
                Canvas.SetLeft(_ellipseFocus, this.Width / 2d - _ellipseFocus.Width / 2d);
                Canvas.SetTop(_ellipseFocus, this.Height / 2d - _ellipseFocus.Height / 2d);
                this.Children.Add(_ellipseFocus);

            }
            else
            {
                _focusMode = false;
                _ellipseFocus.Visibility = Visibility.Collapsed;
            }

        }

    }


}
